#include <string>
#include <iostream>
#include "task_group.h"

// use group_name to define the root task and *root cannot be changed.
TaskGroup::TaskGroup(std::string group_name)
{
  this->group_name = group_name;
  Task *root = new Task(group_name);
  this->root = root;
  this->set_current(nullptr);
}

Task *TaskGroup::get_current()
{
  return this->current;
}

void TaskGroup::set_current(Task *task)
{
  this->current = task;
}

Task *TaskGroup::get_root()
{
  return this->root;
}

std::string TaskGroup::get_group_name()
{
  return this->group_name;
}

void TaskGroup::set_group_name(std::string group_name)
{
  this->group_name = group_name;
}

void TaskGroup::append(std::string text)
{
  // verify task text is right

  // create a new task
  Task *task = new Task(text);
  // check if current is null, if so, set current to task
  Task *current = this->get_current();
  if (current == nullptr)
  {
    task->set_parent(this->root);
    this->root->set_child_head(task);
    this->root->set_child_tail(task);
    this->set_current(task);
  }
  // if current is not null, append task.
  else
  {
    Task *temp = current->get_next();
    current->set_next(task);
    task->set_parent(current->get_parent());
    task->set_prev(current);
    task->set_next(temp);
    if (temp == nullptr)
    {
      current->get_parent()->set_child_tail(task);
    }
    else
    {
      temp->set_prev(task);
    }
    this->move_to_next();
  }
}

void TaskGroup::insert(std::string text)
{
  // verify task text is right

  // create a new task
  Task *task = new Task(text);
  // check if current is null, if so, set current to task
  Task *current = this->get_current();
  if (current == nullptr)
  {
    task->set_parent(this->root);
    this->root->set_child_head(task);
    this->root->set_child_tail(task);
    this->set_current(task);
  }
  // if current is not null, insert task.
  else
  {
    Task *temp = current->get_prev();
    current->set_prev(task);
    task->set_parent(current->get_parent());
    task->set_next(current);
    task->set_prev(temp);
    if (temp == nullptr)
    {
      current->get_parent()->set_child_head(task);
    }
    else
    {
      temp->set_next(task);
    }
    this->move_to_prev();
  }
}

void TaskGroup::add_to_child_first(std::string text)
{
  Task *current = this->get_current();
  if (current == nullptr)
  {
    this->append(text);
    return;
  }
  // verify task text is right

  // create a new task
  // check if current is null, if so, set current to task
  Task *task = new Task(text);
  if (current->get_child_head() == nullptr)
  {
    current->set_child_head(task);
    current->set_child_tail(task);
    task->set_parent(current);
    this->set_current(task);
    return;
  }
  this->set_current(current->get_child_head());
  current->set_prev(task);
  task->set_parent(current->get_parent());
  task->set_next(current);
  current->get_parent()->set_child_head(task);
  this->move_to_prev();
}

void TaskGroup::move_to_next()
{
  Task *current = this->get_current();
  if (current->get_next() == nullptr)
  {
    this->set_current(current->get_parent()->get_child_head());
  }
  else
  {
    this->set_current(current->get_next());
  }
}

void TaskGroup::move_to_prev()
{
  Task *current = this->get_current();
  if (current->get_prev() == nullptr)
  {
    this->set_current(current->get_parent()->get_child_tail());
  }
  else
  {
    this->set_current(current->get_prev());
  }
}

void TaskGroup::move_to_parent()
{
  Task *current = this->get_current();
  if (current->get_parent() == this->root)
    return;
  this->set_current(current->get_parent());
}

void TaskGroup::print_group(Task *temp, unsigned int level)
{
  while (temp != nullptr)
  {
    for (int i = 0; i < level; i++)
    {
      std::cout << "    ";
    }
    if (current == temp)
    {
      std::cout << "*"
                << " ";
    }
    std::cout << temp->get_status() << " ";
    std::cout << temp->get_text() << std::endl;

    if (temp->get_child_head() != nullptr)
    {
      this->print_group(temp->get_child_head(), level + 1);
    }

    temp = temp->get_next();
  }
}

void TaskGroup::delete_current_and_move()
{
  Task *current = this->get_current();

  // when root child is null, set current to null and return.
  if (current == nullptr)
    return;

  // delete current's child tasks
  this->free_tasks_memory(current->get_child_head());

  // when only one task child in task's parent,
  // current and parent's child's head and tail will be null.
  // delete current task and move to parent.
  if (current->get_next() == nullptr && current->get_prev() == nullptr)
  {
    current->get_parent()->set_child_head(nullptr);
    current->get_parent()->set_child_tail(nullptr);

    // if current is root's child, set current to null and return.
    if (current->get_parent() == this->root)
    {
      delete current;
      this->set_current(nullptr);
      return;
    }
    else
    {
      Task *temp = current->get_parent();
      delete current;
      this->set_current(temp);
      return;
    }
  }

  // whether prev is null or not.
  // when current's next is not null, prior to move to next.
  if (current->get_next() != nullptr)
  {
    // when current is the first child in parent's child list.
    if (current->get_prev() == nullptr)
    {
      current->get_parent()->set_child_head(current->get_next());
      current->get_next()->set_prev(nullptr);
    }
    // when prev and next are all not null.
    else
    {
      current->get_next()->set_prev(current->get_prev());
      current->get_prev()->set_next(current->get_next());
    }
    this->set_current(current->get_next());
    delete current;
    return;
  }

  // when only prev is not null.
  if (current->get_prev() != nullptr)
  {
    current->get_prev()->set_next(nullptr);
    current->get_parent()->set_child_tail(current->get_prev());
    this->set_current(current->get_prev());
    delete current;
  }
}

void TaskGroup::free_tasks_memory(Task *temp)
{
  while (temp != nullptr)
  {
    if (temp->get_child_head() != nullptr)
    {
      this->free_tasks_memory(temp->get_child_head());
    }
    Task *temp_next = temp->get_next();
    delete temp;
    temp = temp_next;
  }
}